package resource

import (
	"fmt"
	"sync"
	"time"
)

import (
	"r_log"
	"util"
)

const waitTime = 10    //第二三仓的有效期，据第一仓10个交易日之内
type Resource struct { //每天更新一次，但会多次被重复使用的原始数据
	NextMarketDay util.DateAndTime                   //下一个交易日
	LastValidDay  util.DateAndTime                   //最后一个有效交易日
	NMDLock       sync.RWMutex                       //下一个交易日更新锁
	HolidayList   *Holidays                          //节日表
	HolidayLock   sync.RWMutex                       //节日表更新锁
	StockPools    map[string]*StockPoolsOnEachServer //每台服务器上的账户确认池分开存储
	SpLock        sync.RWMutex
	ModeLevel     [util.PERIOD_COUNT]util.Level //模型评级
	State         ResourceState
	StateLock     sync.RWMutex
}

type ResourceState struct {
	NmdState bool
	HlState  bool
	SpState  bool
	MlState  bool
}

type StockPoolsOnEachServer struct {
	ServerName string
	IsReady    bool
	Stockpools map[util.UserID](*[util.PERIOD_COUNT]*StockPool) //按用户和周期索引确认池
}

/*===========================================
初始化资源，只用于程序启动时。
初始化过程中，资源只加载一次，
任何资源加载失败即返回err，不进行重试
===========================================*/
func NewResource() (*Resource, error) {
	res := new(Resource)
	res.StockPools = make(map[string]*StockPoolsOnEachServer)
	err := res.updateHolidayList()
	if err != nil {
		return nil, fmt.Errorf("res.updateHolidayList(): %v", err)
	}
	err = res.update(0)
	if err != nil {
		return nil, fmt.Errorf("res.update(): %v", err)
	}
	return res, nil
}

/*=========================================
资源更新，用于程序运行过程中。
除假日表外，其他资源均需重置，
除假日表外，任何资源加载失败均触发重试
=========================================*/
func (res *Resource) Update() {
	res.SpLock.Lock()
	res.StockPools = make(map[string]*StockPoolsOnEachServer)
	res.SpLock.Unlock()

	err := res.updateHolidayList()
	if err != nil {
		r_log.Logger.Error("res.updateHolidayList(): %v", err)
	}

	retryTimes := 0
	err = res.update(retryTimes)
	for err != nil {
		r_log.Logger.Error("update(%v):%v", retryTimes, err)
		time.Sleep(time.Second)
		retryTimes++
		err = res.update(retryTimes)
	}
}

/*=======================================
所有资源retryTimes==0时必需进行加载，
当retryTimes>0时，已经加载成功的资源不需要加载
=======================================*/
func (res *Resource) update(retryTimes int) error {
	res.StateLock.Lock()
	state := res.State
	res.StateLock.Unlock()

	var wg sync.WaitGroup
	if retryTimes == 0 || !state.NmdState {
		wg.Add(1)
		go func() {
			defer wg.Done()
			err := res.updateNextMarketDay()
			res.StateLock.Lock()
			if err != nil {
				r_log.Logger.Error("updateNextMarketDay():%v", err)
				res.State.NmdState = util.STATE_ERROR
			} else {
				r_log.Logger.Info("updateNextMarketDay done!")
				res.State.NmdState = util.STATE_READY
			}
			res.StateLock.Unlock()
		}()
	}

	if retryTimes == 0 || !state.MlState {
		wg.Add(1)
		go func() {
			defer wg.Done()
			err := res.updateModeLevel()
			res.StateLock.Lock()
			if err != nil {
				r_log.Logger.Error("updateModeLevel():%v", err)
				res.State.MlState = util.STATE_ERROR
			} else {
				r_log.Logger.Info("updateModeLevel done!")
				res.State.MlState = util.STATE_READY
			}
			res.StateLock.Unlock()
		}()
	}

	if retryTimes == 0 || !state.SpState {
		wg.Add(1)
		go func() {
			defer wg.Done()
			err := res.updateStockPools()
			res.StateLock.Lock()
			if err != nil {
				r_log.Logger.Error("updateStockPools():%v", err)
				res.State.SpState = util.STATE_ERROR
			} else {
				r_log.Logger.Info("updateStockPools done!")
				res.State.SpState = util.STATE_READY
			}
			res.StateLock.Unlock()
		}()
	}
	wg.Wait()

	err := res.updateExitStockList()
	if err != nil {
		r_log.Logger.Error("res.updateExitBoardInfo(): %v", err)
	}

	err = res.updateChooseStockList()
	if err != nil {
		r_log.Logger.Error("res.updateChooseBoardInfo(): %v", err)
	}

	res.StateLock.Lock()
	defer res.StateLock.Unlock()
	if res.State.NmdState && res.State.MlState && res.State.SpState {
		return nil
	}
	return fmt.Errorf("res.update(): update resource failed!")
}

//更新下一个交易日信息和最后一个有效交易日信息
func (res *Resource) updateNextMarketDay() error {
	res.NMDLock.Lock()
	defer res.NMDLock.Unlock()
	//更新下一个交易日信息
	today := util.Today()
	res.NextMarketDay = today
	todayisHoliday, err := res.TodayIsHoliday()
	if err != nil {
		return fmt.Errorf("TodayIsHoliday(): %v", err)
	}
	now := util.GetTime()
	if todayisHoliday || now > util.DateAndTime(150000) {
		nmd, err := res.GetNextMarketDay(today) //获取下一个交易日
		if err != nil {
			return fmt.Errorf("GetNextMarketDay(): %v", err)
		}
		res.NextMarketDay = nmd
	}
	//最后一个有效交易日信息
	res.LastValidDay = res.NextMarketDay
	for diff := waitTime; diff > 0; diff-- {
		nmd, err := res.GetNextMarketDay(res.LastValidDay) //获取下一个交易日
		if err != nil {
			return fmt.Errorf("GetNextMarketDay(): %v", err)
		}
		res.LastValidDay = nmd
	}
	return nil
}
